package com.flex.core.dao;


import java.io.Serializable;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.hibernate.Criteria;
import org.hibernate.LockMode;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import com.flex.constants.SystemConstants;
import com.flex.core.utils.ReflectionUtils;

@SuppressWarnings("unchecked")
@Component
public class BaseHibernateDao<T, PK extends Serializable> {

	protected Logger logger = LoggerFactory.getLogger(getClass());

	@Autowired
	protected SessionFactory sessionFactory;
	
	protected Class<T> entityClass;

	public BaseHibernateDao() {
		this.entityClass = ReflectionUtils.getSuperClassGenricType(getClass());
	}
	
	public void setEntityClass( Class<T> entityClass) {
		this.entityClass = entityClass;
	}

	public Session getSession() {
		return sessionFactory.getCurrentSession();
	}

	/**
	 * 创建或更新一个对象，对象有ID则更新此对象
	 * @param entity
	 */
	public void save(T entity) {
		Assert.notNull(entity);
		getSession().saveOrUpdate(entity);
		//getSession().flush();
	}

	/**
	 * 删除一个对象
	 * @param entity
	 */
	public void delete(T entity) {
		Assert.notNull(entity);
		getSession().delete(entity);
		//getSession().flush();
	}

	/**
	 * 通过主键ID删除一个对象
	 * @param id
	 */
	public void delete(PK id) {
		Assert.notNull(id);
		delete(get(id));
	}

	/**
	 * 通过主键ID来查找一个对象
	 * @param id
	 * @return	找不到对象信息返回null
	 */
	public T get(final PK id) {
		return (T) getSession().get(entityClass, id);
	}
	
	/**
	 * 查找所有数据
	 * @return
	 */
	public List<T> getAll(){
		Criteria criteria = getSession().createCriteria(entityClass);
		criteria.setCacheable(true);
		return criteria.list();
	}
	
	/**
	 * 根据hql语句来查询数据
	 * @param hql		语句
	 * @param values	可变参数
	 * @return
	 */
	public List<T> find(String hql, Object... values) {
		return createQuery(hql, values).list();
	}
	
	/**
	 * 根据hql语句来查询指定条数数据
	 * @param hql			语句
	 * @param pageSize		数据条数
	 * @param values		可变参数
	 * @return
	 */
	public List<T> findBySize(String hql, int pageSize, Object... values) {
		return createQuery(hql, values).setFirstResult(0).setMaxResults(pageSize).list();
	}
	
	/**
	 * 根据hql语句查询一条数据
	 * @param hql		语句
	 * @param values	可变参数
	 * @return
	 */
	public Object findUnique(String hql, Object... values) {
		return createQuery(hql, values).uniqueResult();
	}
	
	/**
	 * 根据对象属性来查询数据
	 * @param propertyName		对象属性，相当于数据库列
	 * @param value	
	 * @return
	 */
	public List<T> findByProperty(String propertyName, Object value) {
		Assert.hasText(propertyName);
		Criteria criteria = getSession().createCriteria(entityClass);
		criteria.add(Restrictions.eq(propertyName, value));
		return criteria.list();
	}
	
	/**
	 * 根据对象属性来查询指定条数的数据
	 * @param propertyName		对象属性，相当于数据库列
	 * @param value
	 * @param pageSize			数据条数
	 * @return
	 */
	public List<T> findByPropertyAndSize(String propertyName, int pageSize, Object value) {
		Assert.hasText(propertyName);
		Criteria criteria = getSession().createCriteria(entityClass);
		criteria.add(Restrictions.eq(propertyName, value));
		return criteria.setFirstResult(0).setMaxResults(pageSize).list();
	}
	
	/**
	 * 根据对象属性来查询一条数据
	 * @param propertyName		对象属性
	 * @param value
	 * @return
	 */
	public T findUniqueByProperty(String propertyName, Object value) {
		Assert.hasText(propertyName);
		Criteria criteria = getSession().createCriteria(entityClass);
		criteria.add(Restrictions.eq(propertyName, value));
		return (T) criteria.uniqueResult();
	}
	
	/**
	 * 判断一个对象的属性是否拥有唯一的数据
	 * @param propertyName		对象属性
	 * @param newValue			属性的值
	 * @return
	 */
	public boolean isPropertyUnique(String propertyName, Object newValue) {
		if (newValue == null)
			return true;
		Object object = findUniqueByProperty(propertyName, newValue);
		return (object == null);
	}
	
	/**
	 * 根据hql语句和参数组装Query查询对象
	 * 
	 * @param queryString
	 *            查询的hql语句
	 * @param values
	 *            查询的参数
	 * @return
	 */
	public Query createQuery(String queryString, Object... values) {
		Assert.hasText(queryString);
		Query queryObject = getSession().createQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryObject.setParameter(i, values[i]);
			}
		}
		queryObject.setCacheable(true);
		return queryObject;
	}
	
	/**
	 * 根据sql语句和参数组装Query查询对象
	 * 
	 * @param queryString
	 *            查询的hql语句
	 * @param values
	 *            查询的参数
	 * @return
	 */
	public Query createSqlQuery(String queryString, Object... values) {
		Assert.hasText(queryString);
		Query queryObject = getSession().createSQLQuery(queryString);
		if (values != null) {
			for (int i = 0; i < values.length; i++) {
				queryObject.setParameter(i, values[i]);
			}
		}
		return queryObject;
	}
	
	/**
	 * 直接执行传入的sql语句
	 * 
	 * @param sql
	 *            要执行的sql语句(select语句除外)
	 */
	public void excuteSql(String queryString, Object... values) {
		this.createSqlQuery(queryString, values).executeUpdate();
	}
	
	public void refresh(Object entity){
		getSession().refresh(entity);
	}
	
	public void refresh(Object entity, final LockMode lockMode){
		Assert.notNull(lockMode);
		getSession().refresh(entity, lockMode);
	}
	
	public void evict(Object entity){
		getSession().evict(entity);
	}
	
	public void flush(){
		getSession().flush();
	}
	
	public void clear(){
		getSession().clear();
	}
	
	/**
	 * 去除hql的select 子句，未考虑union的情况,用于pagedQuery.
	 * 
	 * @see #pagedQuery(String,int,int,Object[])
	 */
	private static String removeSelect(String hql) {
		Assert.hasText(hql);
		int beginPos = hql.toLowerCase().indexOf("from");
		Assert.isTrue(beginPos != -1, " hql : " + hql
				+ " must has a keyword 'from'");
		return hql.substring(beginPos);
	}

	/**
	 * 去除hql的orderby 子句，用于pagedQuery.
	 * 
	 * @see #pagedQuery(String,int,int,Object[])
	 */
	private static String removeOrders(String hql) {
		Assert.hasText(hql);
		Pattern p = Pattern.compile("order\\s*by[\\w|\\W|\\s|\\S]*",
				Pattern.CASE_INSENSITIVE);
		Matcher m = p.matcher(hql);
		StringBuffer sb = new StringBuffer();
		while (m.find()) {
			m.appendReplacement(sb, "");
		}
		m.appendTail(sb);
		return sb.toString();
	}
	
	/**
	 * 分页查询函数，使用hql.
	 * 
	 * @param pageNo
	 *            页号,从1开始.
	 */
	public Page pagedQuery(String hql, int pageNo, int pageSize,
			Object... values) {
		if(pageSize == 0){
			pageSize = SystemConstants.DEFAULT_PAGE_SIZE;
		}
		Assert.hasText(hql);
		Assert.isTrue(pageNo >= 1, "pageNo should start from 1");
		// Count查询
		String countQueryString = " select count (*) "
				+ removeSelect(removeOrders(hql));
		List countlist = find(countQueryString, values);
		long totalCount = 0;
		if (countlist.size() != 0) {
			totalCount = (Long) countlist.get(0);
		}
		if (totalCount < 1)
			return new Page();
		// 实际查询返回分页对象
		int startIndex = Page.getStartOfPage(pageNo, pageSize);
		Query query = createQuery(hql, values);
		List list = query.setFirstResult(startIndex).setMaxResults(pageSize)
				.list();
		return new Page(startIndex, totalCount, pageSize, list);
	}
	
	
}